package me.zingon.core.context;

import me.zingon.core.context.annotation.SAutowired;
import me.zingon.core.context.annotation.SController;
import me.zingon.core.context.annotation.SValue;
import me.zingon.core.context.exception.NoSuchBean;
import me.zingon.core.context.factory.SBeanDefinition;
import me.zingon.core.context.factory.SReader;
import org.yaml.snakeyaml.Yaml;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public class SummerContext {

    private final static String YOURBATIS_FACTORY="yourbatis.mapperFactory";

    private final static String FACTORY_METHOD="create";

    // IoC容器
    private Map<String, Object> factoryBeanInstanceCache = new ConcurrentHashMap<>(256);

    private LinkedHashMap config;

    public SummerContext(String... configLocations) {
        Yaml yml = new Yaml();
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(configLocations[0].replace("classpath:", ""));
        config = (LinkedHashMap) yml.load(is);

        SReader reader=new SReader(config);

        Map<String,SBeanDefinition> beanDefinitions=reader.loadBeanDefinition();

        /**
         * 1.实例化
         */
        doInitNotLazyBean(beanDefinitions);
        /**
         * 2.依赖注入
         */
        doInjection();
    }

    /**
     * 实例化
     */
    void doInitNotLazyBean(Map<String,SBeanDefinition> beanDefinitions){
        Class<?> mapperFactory = null;
        Method create=null;
        try {
            String s=(String) getConfig(YOURBATIS_FACTORY);
            mapperFactory = Class.forName(s);
            create= mapperFactory.getMethod(FACTORY_METHOD,Class.class);
        } catch (ClassNotFoundException | NoSuchMethodException e) {
            e.printStackTrace();
        }

        Method finalCreate = create;
        beanDefinitions.forEach((k, v)->{
            try {
                if(v.getClazz().isInterface()){
                    Object dao = finalCreate.invoke(null,v.getClazz());
                    factoryBeanInstanceCache.put(k,dao);
                }else {
                    factoryBeanInstanceCache.put(k, v.getClazz().newInstance());
                }
            } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        });
    }

    /**
     * 依赖注入
     */
    void doInjection(){
        factoryBeanInstanceCache.forEach((k,v)->{
            Field[] fields = v.getClass().getDeclaredFields();
            for(int i=fields.length-1;i>=0;i--){
                SAutowired s=fields[i].getAnnotation(SAutowired.class);
                if(s!=null){
                    autowiredInject(s,v,fields[i]);
                    continue;
                }

                SValue value=fields[i].getAnnotation(SValue.class);
                if(value!=null){
                    valueInject(value,v,fields[i]);
                }

            }
        });
    }

    /**
     * 注入实例
     * @param s SAutowired注解
     * @param bean 要注入的实例
     * @param field 要注入的成员变量
     */
    void autowiredInject(SAutowired s,Object bean,Field field){
        String beanId=s.value();
        if(beanId.trim().equals("")){
            beanId = field.getName();
        }
        field.setAccessible(true);
        try {
            field.set(bean,getBean(beanId));
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 注入配置文件内容
     * @param value
     * @param bean
     * @param field
     */
    void valueInject(SValue value,Object bean,Field field){
        String beanId=value.value();
        field.setAccessible(true);
        try {
            field.set(bean,getConfig(beanId));
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取实例
     * @param name
     * @return
     */
    public Object getBean(String name){
        return factoryBeanInstanceCache.get(name);
    }

    /**
     * 获取controller
     * @return
     */
    public List<Object> getController(){
        Set<Map.Entry<String,Object>> set = factoryBeanInstanceCache.entrySet();
        return set.stream().filter(item->{
            Class clazz = item.getValue().getClass();
            return clazz.isAnnotationPresent(SController.class);
        }).map(item-> item.getValue()).collect(Collectors.toList());
    }


    private Object getConfig(String key){
        String[] ids=key.split("\\.");
        Map temp=null;
        for(int i=0;i<ids.length-1;i++){
            temp = (Map) config.get(ids[i]);
        }
        return temp.get(ids[ids.length-1]);
    }
}
